﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.BizTalk.Streaming;
using Microsoft.Practices.ESB.Exception.Management;
using Microsoft.Practices.ESB.Resolver;
using Microsoft.Practices.ESB.Resolver.BRE;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.XLANGs.BaseTypes;
using System.Xml;
using Microsoft.RuleEngine;

namespace BizTalk.ESB.Extensions.Resolvers
{
    public class ServiceProxyRequestBreResolver : IResolveProvider
    {
        public Dictionary<string, string> Resolve(ResolverInfo resolverInfo, XLANGMessage message)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyRequestResolution svcpxyResolution = new ServiceProxyRequestResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(resolverInfo.Config, resolverInfo.Resolver);

            try
            {
                if (breInfo.useMsg && breInfo.recognizeMessageFormat)
                {
                    throw new ResolveException("Recognize Message Format not supported.");
                }

                ServiceProxyResolutionManager.SetContext(svcpxyResolution, message);
                XmlDocument msg = null;
                if (breInfo.useMsg)
                {
                    msg = (XmlDocument)message[0].RetrieveAs(typeof(XmlDocument));
                }
                resolverDictionary = ResolveRules(resolverInfo.Config, resolverInfo.Resolver, svcpxyResolution, msg, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }
            // Call ResolveStatic
            return resolverDictionary;
        }

        public Dictionary<string, string> Resolve(string config, string resolver, XmlDocument message)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyRequestResolution svcpxyResolution = new ServiceProxyRequestResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(config, resolver);

            try
            {
                if (breInfo.useMsg && breInfo.recognizeMessageFormat)
                {
                    throw new ResolveException("Recognize Message Format not supported.");
                }
                resolverDictionary = ResolveRules(config, resolver, svcpxyResolution, message, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }
            // Call ResolveStatic
            return resolverDictionary;
        }

        public Dictionary<string, string> Resolve(string config, string resolver, IBaseMessage message, IPipelineContext pipelineContext)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyRequestResolution svcpxyResolution = new ServiceProxyRequestResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(config, resolver);

            // Get Message
            XmlDocument msg = new XmlDocument();
            try
            {
                if (breInfo.useMsg && message.BodyPart != null)
                {
                    Stream originalDataStream = message.BodyPart.GetOriginalDataStream();
                    if (!originalDataStream.CanSeek)
                    {
                        ReadOnlySeekableStream s = new ReadOnlySeekableStream(originalDataStream);
                        message.BodyPart.Data = s;
                        pipelineContext.ResourceTracker.AddResource(s);
                        originalDataStream = s;
                    }

                    if (originalDataStream.Position != 0L)
                    {
                        originalDataStream.Position = 0L;
                    }

                    msg.Load(originalDataStream);
                    originalDataStream.Position = 0L;
                }
                resolverDictionary = ResolveRules(config, resolver, svcpxyResolution, msg, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }

            // Call ResolveStatic
            return resolverDictionary;
        }

        private static Dictionary<String, String> ResolveRules(String config, String resolver, ServiceProxyRequestResolution resolution
            , XmlDocument message, BRE breInfo)
        {
            // DEBUG
            System.Diagnostics.Trace.WriteLine("CONFIG = " + config);
            System.Diagnostics.Trace.WriteLine("RESOLVER = " + resolver);

            Dictionary<String, String> resolverInfo = new Dictionary<String, String>();
            Dictionary<String, String> resolverDictionary = new Dictionary<String, String>();
            Dictionary<String, String> facts = null;

            Int16 PolicyMajorVersion = 0;
            Int16 PolicyMinorVersion = 0;
            String[] VersionArray = null;
            Object[] policyFacts = null;
            Policy brePolicy = null;
            TypedXmlDocument xmlMsgForBre = null;
            String DocType = "Microsoft.Practices.ESB.ResolveProviderMessage";

            if (!resolver.Contains(@":\"))
            {
                resolver = resolver + @":\";
            }
            try
            {
                EventLogger.Write("Resolution strong name is {0}", new object[] { resolution.DocumentSpecStrongName });

                // Set the Doc Type
                if (String.IsNullOrEmpty(resolution.DocumentSpecStrongName) == false && breInfo.recognizeMessageFormat)
                {
                    Int32 index = resolution.DocumentSpecStrongName.IndexOf(",", StringComparison.CurrentCultureIgnoreCase);
                    if ((index > 0) && (index < resolution.DocumentSpecStrongName.Length))
                    {
                        DocType = resolution.DocumentSpecStrongName.Substring(0, index);
                    }
                    else
                    {
                        DocType = resolution.DocumentSpecStrongName;
                    }
                }
                EventLogger.Write("DocType for typed xml document is {0}", new object[] { DocType });

                if (String.IsNullOrEmpty(breInfo.version) == false)
                {
                    VersionArray = breInfo.version.Split(".".ToCharArray());
                    if (VersionArray != null)
                    {
                        PolicyMajorVersion = Convert.ToInt16(VersionArray[0], System.Globalization.NumberFormatInfo.CurrentInfo);
                        PolicyMinorVersion= Convert.ToInt16(VersionArray[1], System.Globalization.NumberFormatInfo.CurrentInfo);
                    }
                }

                if (breInfo.useMsg)
                {
                    policyFacts = new Object[2];
                    xmlMsgForBre = new TypedXmlDocument(DocType, message);
                    policyFacts[0] = resolution;
                    policyFacts[1] = xmlMsgForBre;
                }
                else
                {
                    policyFacts = new Object[] { resolution };
                }

                if (PolicyMajorVersion > 0)
                {
                    brePolicy = new Policy(breInfo.policy, PolicyMajorVersion, PolicyMinorVersion);
                }
                else
                {
                    brePolicy = new Policy(breInfo.policy);
                }

                brePolicy.Execute(policyFacts);
                brePolicy.Dispose();
                if (policyFacts[0] == null)
                {
                    throw new ResolveException("Error executing BRE Resolver");
                }

                resolution = (ServiceProxyRequestResolution)policyFacts[0];
                ServiceProxyResolutionManager.SetResolverDictionary(resolution, resolverDictionary);
                resolverInfo = resolverDictionary;
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (facts != null)
                {
                    facts.Clear();
                    facts = null;
                }
                if (resolution != null)
                {
                    resolution = null;
                }
                if (resolverDictionary != null)
                {
                    resolverDictionary = null;
                }
            }

            return resolverInfo;
        }
    }

    public class ServiceProxyResponseBreResolver : IResolveProvider
    {
        public Dictionary<string, string> Resolve(ResolverInfo resolverInfo, XLANGMessage message)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyResponseResolution svcpxyResolution = new ServiceProxyResponseResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(resolverInfo.Config, resolverInfo.Resolver);

            try
            {
                if (breInfo.useMsg && breInfo.recognizeMessageFormat)
                {
                    throw new ResolveException("Recognize Message Format not supported.");
                }

                ServiceProxyResolutionManager.SetContext(svcpxyResolution, message);
                XmlDocument msg = null;
                if (breInfo.useMsg)
                {
                    msg = (XmlDocument)message[0].RetrieveAs(typeof(XmlDocument));
                }
                resolverDictionary = ResolveRules(resolverInfo.Config, resolverInfo.Resolver, svcpxyResolution, msg, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }
            // Call ResolveStatic
            return resolverDictionary;
        }

        public Dictionary<string, string> Resolve(string config, string resolver, XmlDocument message)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyResponseResolution svcpxyResolution = new ServiceProxyResponseResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(config, resolver);

            try
            {
                if (breInfo.useMsg && breInfo.recognizeMessageFormat)
                {
                    throw new ResolveException("Recognize Message Format not supported.");
                }
                resolverDictionary = ResolveRules(config, resolver, svcpxyResolution, message, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }
            // Call ResolveStatic
            return resolverDictionary;
        }

        public Dictionary<string, string> Resolve(string config, string resolver, IBaseMessage message, IPipelineContext pipelineContext)
        {
            Dictionary<String, String> resolverDictionary;

            // Create instance of ServiceProxyResolution
            ServiceProxyResponseResolution svcpxyResolution = new ServiceProxyResponseResolution();

            // Create instance of BRE
            BRE breInfo = ServiceProxyResolutionManager.CreateResolverDescriptor(config, resolver);

            // Get Message
            XmlDocument msg = new XmlDocument();
            try
            {
                if (breInfo.useMsg && message.BodyPart != null)
                {
                    Stream originalDataStream = message.BodyPart.GetOriginalDataStream();
                    if (!originalDataStream.CanSeek)
                    {
                        ReadOnlySeekableStream s = new ReadOnlySeekableStream(originalDataStream);
                        message.BodyPart.Data = s;
                        pipelineContext.ResourceTracker.AddResource(s);
                        originalDataStream = s;
                    }

                    if (originalDataStream.Position != 0L)
                    {
                        originalDataStream.Position = 0L;
                    }

                    msg.Load(originalDataStream);
                    originalDataStream.Position = 0L;
                }
                resolverDictionary = ResolveRules(config, resolver, svcpxyResolution, msg, breInfo);
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (svcpxyResolution != null)
                {
                    svcpxyResolution = null;
                }
            }

            // Call ResolveStatic
            return resolverDictionary;
        }

        private static Dictionary<String, String> ResolveRules(String config, String resolver, ServiceProxyResponseResolution resolution
            , XmlDocument message, BRE breInfo)
        {
            // DEBUG
            System.Diagnostics.Trace.WriteLine("CONFIG = " + config);
            System.Diagnostics.Trace.WriteLine("RESOLVER = " + resolver);

            Dictionary<String, String> resolverInfo = new Dictionary<String, String>();
            Dictionary<String, String> resolverDictionary = new Dictionary<String, String>();
            Dictionary<String, String> facts = null;

            Int16 PolicyMajorVersion = 0;
            Int16 PolicyMinorVersion = 0;
            String[] VersionArray = null;
            Object[] policyFacts = null;
            Policy brePolicy = null;
            TypedXmlDocument xmlMsgForBre = null;
            String DocType = "Microsoft.Practices.ESB.ResolveProviderMessage";

            if (!resolver.Contains(@":\"))
            {
                resolver = resolver + @":\";
            }
            try
            {
                EventLogger.Write("Resolution strong name is {0}", new object[] { resolution.DocumentSpecStrongName });

                // Set the Doc Type
                if (String.IsNullOrEmpty(resolution.DocumentSpecStrongName) == false && breInfo.recognizeMessageFormat)
                {
                    Int32 index = resolution.DocumentSpecStrongName.IndexOf(",", StringComparison.CurrentCultureIgnoreCase);
                    if ((index > 0) && (index < resolution.DocumentSpecStrongName.Length))
                    {
                        DocType = resolution.DocumentSpecStrongName.Substring(0, index);
                    }
                    else
                    {
                        DocType = resolution.DocumentSpecStrongName;
                    }
                }
                EventLogger.Write("DocType for typed xml document is {0}", new object[] { DocType });

                if (String.IsNullOrEmpty(breInfo.version) == false)
                {
                    VersionArray = breInfo.version.Split(".".ToCharArray());
                    if (VersionArray != null)
                    {
                        PolicyMajorVersion = Convert.ToInt16(VersionArray[0], System.Globalization.NumberFormatInfo.CurrentInfo);
                        PolicyMinorVersion = Convert.ToInt16(VersionArray[1], System.Globalization.NumberFormatInfo.CurrentInfo);
                    }
                }

                if (breInfo.useMsg)
                {
                    policyFacts = new Object[2];
                    xmlMsgForBre = new TypedXmlDocument(DocType, message);
                    policyFacts[0] = resolution;
                    policyFacts[1] = xmlMsgForBre;
                }
                else
                {
                    policyFacts = new Object[] { resolution };
                }

                if (PolicyMajorVersion > 0)
                {
                    brePolicy = new Policy(breInfo.policy, PolicyMajorVersion, PolicyMinorVersion);
                }
                else
                {
                    brePolicy = new Policy(breInfo.policy);
                }

                brePolicy.Execute(policyFacts);
                brePolicy.Dispose();
                if (policyFacts[0] == null)
                {
                    throw new ResolveException("Error executing BRE Resolver");
                }

                resolution = (ServiceProxyResponseResolution)policyFacts[0];
                ServiceProxyResolutionManager.SetResolverDictionary(resolution, resolverDictionary);
                resolverInfo = resolverDictionary;
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (facts != null)
                {
                    facts.Clear();
                    facts = null;
                }
                if (resolution != null)
                {
                    resolution = null;
                }
                if (resolverDictionary != null)
                {
                    resolverDictionary = null;
                }
            }

            return resolverInfo;
        }
    }
}
